home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
PC World Komputer 2010 April
/
PCWorld0410.iso
/
pluginy Firefox
/
7661
/
7661.xpi
/
components
/
RILofflineQueue.js
< prev
next >
Wrap
Text File
|
2009-12-11
|
14KB
|
447 lines
/*
License: This source code may not be used in other applications whether they
be personal, commercial, free, or paid without written permission from Read It Later.
/////////
DEVELOPER API: readitlaterlist.com/api/
/////////
If you would like to customize Read It Later or build an application that works with
Read it Later take a look at the READ IT LATER OPEN API:
http://readitlaterlist.com/api/
Suggestions for additions to Read It Later are VERY welcome. A large number of user
suggestions have been implemented. Please let me know of any additional features you
are seeking at: http://readitlaterlist.com/support/
Thanks
*/
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
function RILofflineQueue() {
this.wrappedJSObject = this;
this.maxThreads = 2; // increasing this will speed up downloading but will slow down firefox performance
this.threads = [];
}
// class definition
RILofflineQueue.prototype = {
// properties required for XPCOM registration:
classDescription: "Read It Later Offline Queue Javascript XPCOM Component",
classID: Components.ID("{a6b4c920-aab2-11de-8a39-0800200c9a66}"),
contractID: "@ril.ideashower.com/rilofflinequeue;1",
QueryInterface: XPCOMUtils.generateQI(),
//////////////////////////////////
init : function()
{
this.APP = Components.classes['@ril.ideashower.com/rildelegate;1'].getService().wrappedJSObject;
this.PREFS = Components.classes['@ril.ideashower.com/rilprefs;1'].getService().wrappedJSObject;
this.JSON = Components.classes["@mozilla.org/dom/json;1"].createInstance(Components.interfaces.nsIJSON);
this.OBS = Components.classes["@mozilla.org/observer-service;1"].getService(Components.interfaces.nsIObserverService);
},
start : function(resetQueue, doNotLoadQueue)
{
if (resetQueue && this.downloading) return -1;
this.downloading = true;
if (!doNotLoadQueue)
this.loadQueue(this.PREFS.getBool('getOfflineWeb'), this.PREFS.getBool('getOfflineText'), resetQueue);
if (!this.queue || this.queue.length == 0) {
this.downloading = false;
return -2;
}
// Create threads
if (resetQueue || !this.threads || this.threads.length == 0)
this.createThreads(true);
return true;
},
makeSureQueueIsInit : function(force)
{
if (force || !this.queue)
{
this.pointer = 0;
this.counters = {success:0,failed:0};
this.queue = [];
this.idsInTheQueue = {'2':{},'1':{}};
}
},
loadQueue : function(downloadWeb, downloadText, resetQueue)
{
this.makeSureQueueIsInit(resetQueue);
let item, downloadingAtLeastOneView;
// sort list so newest is first
let sortedList = this.APP.sortList(this.APP.LIST.list.slice());
for(let i in sortedList)
{
downloadingAtLeastOneView = false;
item = sortedList[i];
this.addItemToQueue(item, {web:downloadWeb,text:downloadText});
}
},
addItemToQueue : function(item, views, startIfNotStarted)
{
this.makeSureQueueIsInit();
// if it's already in the queue, skip it
if (!views) views = {web:this.PREFS.getBool('getOfflineWeb'), text:this.PREFS.getBool('getOfflineText')};
// add specific views to queue
if (views.web && item.offlineWeb != 1 && !this.idsInTheQueue[2][item.itemId])
{
this.addToQueue(item.itemId, item.url, 2, startIfNotStarted);
}
if (views.text && item.offlineText != 1 && !this.idsInTheQueue[1][item.itemId])
{
this.addToQueue(item.itemId, item.url, 1, startIfNotStarted);
}
},
addToQueue : function(itemId, url, type, startIfNotStarted)
{
if (this.clearingOffline) return false;
let downloader;
if (type == 1)
downloader = Components.classes["@ril.ideashower.com/riltextdownloader;1"].createInstance(Components.interfaces.nsIRILtextDownloader);
else if (type == 2)
downloader = Components.classes["@ril.ideashower.com/rilwebdownloader;1"].createInstance(Components.interfaces.nsIRILwebDownloader);
if (downloader)
{
downloader.init( itemId, url );
this.queue.push( downloader );
this.idsInTheQueue[type][itemId] = true;
}
if (startIfNotStarted) {
if (!this.downloading) return this.start(true, true);
// else
if (!this.next()) {
this.updateProgress();
}
}
},
createThreads : function(loadNextRightAway)
{
this.threads = [];
for(let i=0; i<this.maxThreads; i++) {
this.threads[i] = {
id : i,
inUse : false
}
if (loadNextRightAway) this.next();
}
},
next : function()
{
// check if still online
let mainWindow = this.APP.getMainWindow();
if (mainWindow && mainWindow.navigator && !mainWindow.navigator.onLine)
{ // if it can't access the navigator, assume still online
this.cancel();
return;
}
let nextItem = this.queue[this.pointer];
if ( nextItem )
{
// make sure the item still exists (wasn't marked as read)
let item = this.APP.LIST.itemById(nextItem.itemId);
if (!item)
{
this.pointer++; //skip it
return this.next();
}
let thread = this.getAnOpenThread();
if (thread) {
this.pointer++;
this.updateProgress();
//this.d("\nstarting "+nextItem.url);
// load thread
thread.downloader = nextItem;
nextItem.start(thread.id);
return true;
} // no threads are open, do not advance the pointer, after a thread completes it will come back here
}
else
{
//this.d('nothing to next');
for(let i in this.threads)
{
if (this.threads[i].inUse) {
//dump("\n\nthread still open")
//this.d('thread '+i+ ' is still open: '+this.threads[i].downloader.itemId + ' : ' +this.threads[i].downloader.url )
return; // a thread is still processing, do not end queue yet
}
}
// Complete
//dump("\n\n\nQUEUE IS DONE")
this.queueIsDone();
}
},
getAnOpenThread : function() {
if (!this.threads) this.createThreads();
for(let i in this.threads)
{
if (!this.threads[i].inUse) {
this.threads[i].inUse = true;
return this.threads[i];
}
}
},
updateProgress : function(turnOff)
{
if (!this.queue) this.queue = [];
this.APP.updateDownloadProgress( turnOff?-1:this.pointer , this.queue.length );
},
textFinished : function(downloader)
{
this.itemIsDone(downloader.itemId, downloader.type, downloader.threadId, downloader.success, downloader.statusCode);
},
itemIsDone : function(itemId, type, threadId, success, statusCode, retainDomains)
{
if (this.downloading)
{
// make sure the item still exists (wasn't marked as read)
let item = this.APP.LIST.itemById(itemId);
if (item)
{
//this.d("\n\nitem is done | \n threadId:" + threadId + " \nsuccess: " + success + " \n url: " + item.url + " \n type: " + type + "\nstatusCode: " + statusCode + ' | ' + retainDomains);
this.APP.LIST.updateOffline(itemId, type, statusCode, retainDomains);
this.counters[ success ? 'success' : 'failed' ]++;
}
this.threads[ threadId ].inUse = false;
this.next();
}
},
queueIsDone : function()
{
//this.d('queueIsDone');
if (this.downloading)
{
this.pointer = this.queue.length+1; //set it to +1 over the queue length so updateProgress knows we are done
this.updateProgress();
this.downloading = false;
this.threads = {};
this.queue = null;
}
},
cancel : function()
{
this.downloading = false;
this.updateProgress(true);
for(let i in this.threads)
{
if (this.threads[i].downloader)
this.threads[i].downloader.cancel();
}
this.threads = false;
this.queue = null;
},
// -- //
downloadTextWrapper : function(itemId, url, delegate, doc)
{
if (!this.textViewDownloads) this.textViewDownloads = {};
let set = {doc: doc, delegate: delegate};
set.downloader = Components.classes["@ril.ideashower.com/riltextdownloader;1"].createInstance(Components.interfaces.nsIRILtextDownloader);
this.textViewDownloads[set.downloader.init( itemId, url, 'textViewReady')] = set;
set.downloader.start(null);
},
textViewReady : function(downloader)
{
let set = this.textViewDownloads[ downloader.requestId ];
if (set.delegate)
set.delegate['textViewReady'].call(set.delegate, downloader, set.doc);
},
// -- Writing / Files
write : function(path, data, noEncoding, delegate, selector)
{
try {
// Create file paths
let file = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
file.initWithPath( path );
// Create needed directories to file
if (!file.exists()) file.createUnique( Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0777);
// - Async write
// Then, we need an output stream to our output file.
var ostream = Components.classes["@mozilla.org/network/file-output-stream;1"].
createInstance(Components.interfaces.nsIFileOutputStream);
ostream.init(file, -1, -1, 0);
var istream;
if (noEncoding)
{
// Finally, we need an input stream to take data from.
istream = Components.classes["@mozilla.org/io/string-input-stream;1"].
createInstance(Components.interfaces.nsIStringInputStream);
istream.setData(data, data.length);
}
else {
// Obtain a converter to convert our data to a UTF-8 encoded input stream.
let converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"]
.createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
converter.charset = "UTF-8";
// Asynchronously copy the data to the file.
istream = converter.convertToInputStream(data);
}
// Start saving
this.asyncCopy(istream, ostream, this.APP.genericClosure(delegate, selector));
return true;
} catch(e) {
Components.utils.reportError('Error saving file: ' + path + "\n" + e);
}
},
// copied from http://mxr.mozilla.org/mozilla-central/source/netwerk/base/src/NetUtil.jsm#77
// which replaces https://developer.mozilla.org/en/JavaScript_code_modules/NetUtil.jsm#asyncCopy
// TODO when 3.6 is out, this will only be required for older browsers
asyncCopy: function (aSource, aSink, aCallback)
{
if (!aSource || !aSink) {
let exception = new Components.Exception(
"Must have a source and a sink",
Cr.NS_ERROR_INVALID_ARG,
Components.stack.caller
);
throw exception;
}
var sourceBuffered = true;//ioUtil.inputStreamIsBuffered(aSource);
var sinkBuffered = true;//ioUtil.outputStreamIsBuffered(aSink);
var ostream = aSink;
if (!sourceBuffered && !sinkBuffered) {
// wrap the sink in a buffered stream.
ostream = Components.classes["@mozilla.org/network/buffered-output-stream;1"].
createInstance(Components.interfaces.nsIBufferedOutputStream);
ostream.init(aSink, 0x8000);
sinkBuffered = true;
}
// make a stream copier
var copier = Components.classes["@mozilla.org/network/async-stream-copier;1"].
createInstance(Components.interfaces.nsIAsyncStreamCopier);
// Initialize the copier. The 0x8000 should match the size of the
// buffer our buffered stream is using, for best performance. If we're
// not using our own buffered stream, that's ok too. But maybe we
// should just use the default net segment size here?
copier.init(aSource, ostream, null, sourceBuffered, sinkBuffered,
0x8000, true, true);
var observer;
if (aCallback) {
observer = {
onStartRequest: function(aRequest, aContext) {},
onStopRequest: function(aRequest, aContext, aStatusCode) {
let success = (Components.isSuccessCode(aStatusCode));
aCallback(success);
}
}
} else {
observer = null;
}
// start the copying
copier.asyncCopy(observer, null);
return copier;
},
// -- //
setOfflineStatus : function(action, state)
{
this[action+'Offline'] = state;
// update assets directories
if (action == 'moving' && !state)
this.APP.ASSETS.init();
// update options window
this.APP.commandInAllOpenWindows('RILoptions', 'offlineStatusChanged', null, true, true);
},
// -- //
d : function(str) { return dump(str+"\n"); },
};
var components = [RILofflineQueue];
function NSGetModule(compMgr, fileSpec) {
return XPCOMUtils.generateModule(components);
}